package com.shareware.common.utils

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.*
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.provider.Settings
import android.telephony.*
import android.text.TextUtils
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.shareware.common.DEFAULT
import com.shareware.common.annotation.NetState
import com.shareware.common.getContextGlobal
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.NetworkInterface
import java.util.*

/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */

interface INetChangeProtocol {

    fun netChanged(@NetState state: Int)

    companion object {

        const val NETWORK_DEFAULT = -3

        //没有连接网络
        const val NETWORK_NONE: Int = 0

        //网络可用
        const val NETWORK_AVAILABLE: Int = 3

        //移动数据
        const val NETWORK_MOBILE = 1
        const val NETWORK_MOBILE_OFF = -1

        //Wifi
        const val NETWORK_WIFI = 2
        const val NETWORK_WIFI_OFF = -2
    }
}

private val netChangedListener = HashSet<INetChangeProtocol>()

/**
 * 注册网络状态变化监听
 */
fun registerNetChangeListener(listener: INetChangeProtocol) {
    if (listener is Activity || listener is Fragment) {
        log(DEFAULT, "multiple registration")
        return
    }
    netChangedListener.add(listener)
}

/**
 * 注销网络状态变化监听
 */
fun unregisterNetChangeListener(listener: INetChangeProtocol) {
    netChangedListener.remove(listener)
}

/**
 * 打开网络设置界面
 */
fun Context.openWifiSetting() {
    val intent = Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
    startActivity(intent)
}

/**
 * @exception android 5.1.1,level 22 VIVO X7 vivo/FUNTOUCH crash: No such file or directory
 * @return 得到ip地址
 */
fun getLocalIp4Address(): String {
    try {
        val en = NetworkInterface.getNetworkInterfaces()
        while (en.hasMoreElements()) {
            val networkInterface = en.nextElement()
            val enumIpAddress = networkInterface.inetAddresses
            while (enumIpAddress.hasMoreElements()) {
                val netAddress = enumIpAddress.nextElement()
                if (!netAddress.isLoopbackAddress && netAddress is Inet4Address) {
                    return netAddress.hostAddress ?: ""
                }
            }
        }
    } catch (ex: Exception) {
        return ""
    }
    return ""
}

fun getLocalIp6Address(): String {
    try {
        val en = NetworkInterface.getNetworkInterfaces()
        while (en.hasMoreElements()) {
            val networkInterface = en.nextElement()
            val enumIpAddress = networkInterface.inetAddresses
            while (enumIpAddress.hasMoreElements()) {
                val netAddress = enumIpAddress.nextElement()
                if (!netAddress.isLoopbackAddress && netAddress is Inet6Address) {
                    return netAddress.hostAddress ?: ""
                }
            }
        }
    } catch (ex: Exception) {
        return ""
    }
    return ""
}

/**
 * 判断是否有网络连接
 * @return boolean
 */
fun isNetworkConnected(): Boolean {
    val info = try {
        (getContextGlobal().getSystemService(
            Context.CONNECTIVITY_SERVICE
        ) as ConnectivityManager).activeNetworkInfo
    } catch (ignore: Exception) {
        null
    }
    return info != null && info.isConnected && info.isAvailable
}

/**
 * 网络是否代理
 */
private fun isProxy(ctx: Context): Boolean {
    val proxyAddress = System.getProperty("http.proxyHost")
    val portStr = System.getProperty("http.proxyPort")
    val proxyPort = (portStr ?: "-1").toInt()
    return !TextUtils.isEmpty(proxyAddress) && proxyPort != -1
}

/**
 * 是否正在使用VPN
 */
private fun isVpnUsed(): Boolean {
    try {
        val niList: Enumeration<*>? = NetworkInterface.getNetworkInterfaces()
        if (niList != null) {
            for (obj in Collections.list(niList)) {
                val netInter = obj as NetworkInterface
                if (!netInter.isUp || netInter.interfaceAddresses.size == 0) {
                    continue
                }
                if ("tun0" == netInter.name || "ppp0" == netInter.name) {
                    return true
                }
            }
        }
    } catch (e: Throwable) {
        e.printStackTrace()
    }
    return false
}

/**
 * 获取信号量,需要定位权限，如果无需定位，则使用PhoneStateListener
 * @return -1为无定位权限
 */
fun getNetSignalStrength(): Int {
    var signal = -1
    if (ActivityCompat.checkSelfPermission(
            getContextGlobal(),
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        return signal
    }
    val phoneManager =
        getContextGlobal().getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
            ?: return signal
    phoneManager.allCellInfo?.forEach cellFor@{
        when (it) {
            is CellInfoLte -> {
                signal = it.cellSignalStrength.dbm
                return@cellFor
            }

            is CellInfoGsm -> {
                signal = it.cellSignalStrength.dbm
                return@cellFor
            }

            is CellInfoCdma -> {
                signal = it.cellSignalStrength.dbm
                return@cellFor
            }

            is CellInfoWcdma -> {
                signal = it.cellSignalStrength.dbm
                return@cellFor
            }
        }
    }
    return signal
}

fun getNetWorkType(): NetType {
    if (!isNetworkConnected()) {
        return NetType.TYPE_NULL
    }
    if (getNetWorkState() == INetChangeProtocol.NETWORK_WIFI) {
        return NetType.TYPE_WIFI
    }
    var networkType: Int? = TelephonyManager.NETWORK_TYPE_UNKNOWN
    tryMethodIgnore {
        (getContextGlobal().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).let {
            networkType =
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M && (ContextCompat.checkSelfPermission(
                        getContextGlobal(),
                        Manifest.permission.READ_PHONE_STATE
                    ) == PackageManager.PERMISSION_GRANTED && it.hasCarrierPrivileges())
                ) {
                    it.dataNetworkType
                } else {
                    it.networkType
                }
        }
    }
    if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
        tryMethodIgnore {
            (getContextGlobal().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).let {
                it.activeNetworkInfo?.let { netInfo ->
                    networkType = netInfo.subtype
                }
            }
        }
    }
    return when (networkType) {
        TelephonyManager.NETWORK_TYPE_GPRS,
        TelephonyManager.NETWORK_TYPE_EDGE,
        TelephonyManager.NETWORK_TYPE_CDMA,
        TelephonyManager.NETWORK_TYPE_1xRTT,
        TelephonyManager.NETWORK_TYPE_IDEN,
        -> NetType.TYPE_2G

        TelephonyManager.NETWORK_TYPE_UMTS,
        TelephonyManager.NETWORK_TYPE_EVDO_0,
        TelephonyManager.NETWORK_TYPE_EVDO_A,
        TelephonyManager.NETWORK_TYPE_HSDPA,
        TelephonyManager.NETWORK_TYPE_HSUPA,
        TelephonyManager.NETWORK_TYPE_HSPA,
        TelephonyManager.NETWORK_TYPE_EVDO_B,
        TelephonyManager.NETWORK_TYPE_EHRPD,
        TelephonyManager.NETWORK_TYPE_HSPAP,
        -> NetType.TYPE_3G

        TelephonyManager.NETWORK_TYPE_LTE,
        TelephonyManager.NETWORK_TYPE_IWLAN,
        19,
        -> NetType.TYPE_4G

        20 -> NetType.TYPE_5G
        else -> NetType.TYPE_UNKNOWN
    }
}

/**
 * 返回当前网络状态
 * @return int
 */
fun getNetWorkState(): Int {
    val connectManager =
        getContextGlobal().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
        val networkCapabilities =
            connectManager.getNetworkCapabilities(connectManager.activeNetwork)
                ?: return INetChangeProtocol.NETWORK_NONE

        if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
            return INetChangeProtocol.NETWORK_WIFI
        }
        if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
            return INetChangeProtocol.NETWORK_MOBILE
        }
    } else
        connectManager.allNetworks.forEach {
            val networkInfo = connectManager.getNetworkInfo(it)
            if (networkInfo != null && networkInfo.isConnected && networkInfo.isAvailable) {
                return when (networkInfo.type) {
                    ConnectivityManager.TYPE_WIFI -> INetChangeProtocol.NETWORK_WIFI
                    ConnectivityManager.TYPE_MOBILE -> INetChangeProtocol.NETWORK_MOBILE
                    else -> INetChangeProtocol.NETWORK_NONE
                }
            }
        }
    return INetChangeProtocol.NETWORK_NONE
}

internal object NetConnectService {

    private var networkCallback: NetworkCallbackImpl? = null

    private val netHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            if (msg.what == 0) {
                netChangeNotice(msg.arg1)
            }
        }
    }

    private fun netChangeNotice(state: Int) {
        tryMethodIgnore {
            ActivityExt.getNetChangeImpl().forEach { activity ->
                activity.runOnUiThread {
                    if (activity is FragmentActivity) {
                        activity.supportFragmentManager.fragments.forEach { fragment ->
                            if (fragment is INetChangeProtocol) {
                                fragment.netChanged(state)
                            }
                        }
                    }
                    (activity as INetChangeProtocol).netChanged(state)
                }
            }
            val context: Context = getContextGlobal()
            if (context is INetChangeProtocol) {
                context.netChanged(state)
            }
            netChangedListener.forEach {
                it.netChanged(state)
            }
        }
    }

    fun register() {
        if (networkCallback != null) {
            return
        }
        networkCallback = NetworkCallbackImpl().also {
            (getContextGlobal().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
                .registerNetworkCallback(NetworkRequest.Builder().build(), it)
        }
    }

    @SuppressLint("NewApi")
    fun release() {
        networkCallback?.let {
            (getContextGlobal().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
                .unregisterNetworkCallback(it)
            networkCallback = null
        }
    }

    @Volatile
    private var netState: Int = INetChangeProtocol.NETWORK_DEFAULT

    /**
     * 这里延迟处理，至于后台，网络变化时系统会记录所有状态，恢复前台时会一起返回大量数据
     */
    private fun netChange(@NetState state: Int) {
        if (state == netState) {
            return
        }
        netState = state
        if (netHandler.hasMessages(0)) {
            netHandler.removeMessages(0)
        }
        val msg = Message()
        msg.what = 0
        msg.arg1 = state
        netHandler.sendMessageDelayed(msg, 100)
    }

    private class NetworkCallbackImpl : ConnectivityManager.NetworkCallback() {

        private val netMap = HashMap<String, Int?>()
        private var hasNet = false

        override fun onAvailable(network: Network) {//网络可用
            if (!hasNet) {
                hasNet = true
                netChange(INetChangeProtocol.NETWORK_AVAILABLE)
            }
        }

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities,
        ) {
            tryMethodIgnore {
                when {
                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
                        netMap[network.toString()] = INetChangeProtocol.NETWORK_WIFI
                        netChange(INetChangeProtocol.NETWORK_WIFI)
                    }

                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
                        netMap[network.toString()] = INetChangeProtocol.NETWORK_MOBILE
                        netChange(INetChangeProtocol.NETWORK_MOBILE)
                    }

                    else -> log(DEFAULT, networkCapabilities.toString())
                }
            }
        }

        override fun onLost(network: Network) {
            val netState = netMap[network.toString()]
            netState?.let {
                netChange(
                    if (netState == INetChangeProtocol.NETWORK_WIFI) {
                        INetChangeProtocol.NETWORK_WIFI_OFF
                    } else {
                        INetChangeProtocol.NETWORK_MOBILE_OFF
                    }
                )
                netMap.remove(network.toString())
                if (netMap.isEmpty()) {
                    netChange(INetChangeProtocol.NETWORK_NONE)
                    hasNet = false
                }
            }
        }
    }
}

enum class NetType(val type: Int, val value: String) {
    TYPE_NULL(0, "NULL"), TYPE_UNKNOWN(1, "UNKNOWN"),
    TYPE_WIFI(1 shl 1, "WIFI"), TYPE_2G(1 shl 2, "2G"),
    TYPE_3G(1 shl 3, "3G"), TYPE_4G(1 shl 4, "4G"), TYPE_5G(1 shl 5, "5G")
}

